package org.study.local.boot.controller;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.IdUtil;
import com.google.common.collect.Lists;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.study.local.boot.config.LocalFileProperties;
import org.study.local.boot.model.FileUploadRequest;
import org.study.local.boot.util.FileUploadUtils;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 文件上传、下载
 * 参考 https://www.hangge.com/blog/cache/detail_2463.html
 * 参考 https://blog.csdn.net/weixin_44953227/article/details/116013637
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2024年05月18日
 */
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@RestController
@RequestMapping("/upload")
public class FileUploadController {
    private final LocalFileProperties localFileProperties;

    /**
     * 使用 postman 测试：有无 @RequestParam 注解都可以，但请求参数必须是 form-data 格式
     *
     * @param request
     * @param file
     */
    @PostMapping("/one")
    public String uploadOne(HttpServletRequest request, @RequestParam("file") MultipartFile file) {
        String filePath = this.doUpload(request, file);
        return filePath;
    }

    /**
     * 使用 postman 测试：有无 @RequestParam 注解都可以，但请求参数必须是 form-data 格式
     *
     * @param request
     * @param fileList
     */
    @PostMapping("/many")
    public List<String> uploadMany(HttpServletRequest request, @RequestParam("fileList") MultipartFile[] fileList) {
        List<String> filePathList = Lists.newArrayList();
        for (MultipartFile uploadFile : fileList) {
            String filePath = this.doUpload(request, uploadFile);
            filePathList.add(filePath);
        }
        return filePathList;
    }

    /**
     * 使用 postman 测试：有无 @RequestParam 注解都可以，但请求参数必须是 form-data 格式
     *
     * @param request
     * @param name
     * @param mainPic
     * @param detailPic
     */
    @PostMapping("/manyParam")
    public Map<String, Object> uploadPojo(HttpServletRequest request, @RequestParam("name") String name, @RequestParam("mainPic") MultipartFile mainPic, @RequestParam("detailPic") MultipartFile[] detailPic) {
        String mainPicPath = this.doUpload(request, mainPic);
        List<String> detailPicPathList = Lists.newArrayList();
        for (MultipartFile uploadFile : detailPic) {
            String detailPicPath = this.doUpload(request, uploadFile);
            detailPicPathList.add(detailPicPath);
        }
        Map<String, Object> dataMap = new HashMap<>(3) {{
            this.put("name", name);
            this.put("mainPic", mainPicPath);
            this.put("detailPic", detailPicPathList);
        }};
        return dataMap;
    }

    /**
     * 使用 postman 测试：有无 @RequestParam 注解都可以，但请求参数必须是 form-data 格式
     *
     * @param request
     * @param uploadRequest
     */
    @PostMapping("/pojo")
    public Map<String, Object> uploadPojo(HttpServletRequest request, FileUploadRequest uploadRequest) {
        String mainPicPath = this.doUpload(request, uploadRequest.getMainPic());
        List<String> detailPicPathList = Lists.newArrayList();
        for (MultipartFile uploadFile : uploadRequest.getDetailPic()) {
            String detailPicPath = this.doUpload(request, uploadFile);
            detailPicPathList.add(detailPicPath);
        }
        Map<String, Object> dataMap = new HashMap<>(3) {{
            this.put("name", uploadRequest.getName());
            this.put("mainPic", mainPicPath);
            this.put("detailPic", detailPicPathList);
        }};
        return dataMap;
    }

    @PostMapping("/requestPart")
    public String uploadImage(HttpServletRequest request, @RequestPart("file") MultipartFile file) {
        String filePath = this.doUpload(request, file);
        return filePath;
    }

    public String doUpload(HttpServletRequest request, MultipartFile uploadFile) {
        // 判断文件上传后的存储目录是否存在，不存在则创建
        String group = DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN).format(LocalDate.now());
        File folder = new File(localFileProperties.getUploadFolder() /*+ File.separator*/ + group);
        if (!folder.isDirectory()) {
            folder.mkdirs();
        }
        // 校验文件格式、大小
        boolean fileSizeOver = FileUploadUtils.isFileSizeOver(uploadFile, localFileProperties.getMaxSize(), FileUploadUtils.SizeUnit.MB);
        boolean fileTypeAllowed = FileUploadUtils.isFileTypeAllowed(uploadFile, localFileProperties.getAllowTypes());
        // 获取原始文件名
        String srcFilename = uploadFile.getOriginalFilename();
        // 新文件名
        // 1、直接使用原来的文件名
        String filename1 = srcFilename;
        // 2、为避免重名，使用UUID生成新的文件名
        String filename = IdUtil.getSnowflakeNextIdStr() + srcFilename.substring(srcFilename.lastIndexOf("."), srcFilename.length());
        try {
            // 上传的文件在本地的存储路径
            File localFilePath = new File(folder, filename);
            log.info("上传的文件：{} 在本地的存放路径：{}", srcFilename, localFilePath.getAbsolutePath());
            // 上传文件
            uploadFile.transferTo(localFilePath);
            // 因为在 SystemWebMvcConfigurer 类中重写了 WebMvcConfigurer.addResourceHandlers() 方法，实现了本地文件路径的映射
            String filePath1 = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + localFileProperties.getWebPathPrefix() + "/" + group + "/" + filename;
            // 因为配置的 spring.web.resources.static-locations 中包含 file:D:/upload/file/ 本地文件路径，实现了本地文件路径的映射
            String filePath2 = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/" + group + "/" + filename;
            log.info("上传的文件：{}，在浏览器中的访问路径：{} 或 {}", srcFilename, filePath1, filePath2);
            // 将访问地址返回
            return filePath1;
        } catch (IOException e) {
            log.error("上传文件异常", e);
            throw new RuntimeException("上传文件异常");
        }
    }
}